Repository navigation
Conversation
|
Thanks. I read #3441, #3442 and ff73119. If I understand the concern: the configure that adds If so, I can rework this along the lines you proposed in #3442: the application tells winit it has stopped presenting (for example, a window method called after handling |
Hyprland doesn't implement transactional configure sequences. It cannot be used to test anything related to configure sequences since it doesn't operate under the same constraints as other compositors.
Most compositors use a short timeout, say 200 milliseconds. So this would show up as a stutter instead of permanently blocking subsequent configuration sequences. |
|
Thanks, both. I tested this on compositors with transactional configure sequences and on ones that send
To model the case you describe, I changed Jay to also wait on hidden windows (200 ms timeout). There the patch caused one timeout-long stall, about 200 ms, each time a window was hidden, and no permanent block. Stock winit avoids that stall only because the app keeps drawing once and then freezes on vsync for as long as the window is hidden. A single empty commit from winit on the configure that leaves the window suspended removed every stall in that setup. sctk already acks each configure, so the commit is the only missing piece. A commit from winit is only safe while the app isn't presenting, though, so I'd follow the shape from #3442: a window method the app calls after handling |
A compositor sends no frame callbacks to a surface it has suspended, so a client presenting with vsync blocks until the window is shown again. Winit stopped reporting this state as `WindowEvent::Occluded` in rust-windowing#3441: every configure also requested a redraw so the acknowledgement would be committed, and drawing on the configure that suspends the window blocked. Emit `Occluded` again when the `suspended` state is added or removed, and add `WindowExtWayland::notify_presentation_paused`, as proposed in rust-windowing#3442. An application calls it after it has stopped presenting on `Occluded(true)`. Winit then commits the surface itself for the pending configure (sctk has already acknowledged it) and for every later configure while the window stays suspended, instead of asking for a redraw. The configure that ends the suspension asks for a redraw as before, and the next `Occluded(true)` needs a new call. Winit leaves the commit to the application when the integer buffer scale changed since the call, because the new scale would apply to the old buffer, whose size need not be a multiple of it. An application that never calls the method behaves as before: every configure, including the suspending one, asks for a redraw. Refs rust-windowing#3442
9d2015f to
a68ac90
Compare
|
I pushed the shape from my last comment.
Testing used a small softbuffer probe that hides its window three times per run. On headless Jay, I kept hidden windows in transactions as before, with the stock 50 ms timeout:
On Hyprland 0.56.2 (headless output, window moved to a special workspace and back), the events and commits were as expected. The 0.30 backport (#4710) gave the same results. @mahkoh, a side note: Jay's headless backend never clears The name is open. I can also move it to the core |
Hyprland stops frame callbacks to a window covered by a fullscreen or maximized one without marking it suspended, so a vsync swap blocked the event loop until the compositor called ZapFast unresponsive (#190). Pin the shared forks: crmne/egui apps-0.36 presents without a blocking swap on Wayland, paces frames by frame callbacks, and runs only the app logic when a redraw is 250 ms overdue (emilk/egui#8631); crmne/winit apps-0.30 reports suspended as Occluded and commits on the app's behalf (rust-windowing/winit#4709). vsync.rs's compositor check goes: vsync is requested everywhere and eframe handles Wayland. Fixes #190
Hyprland sends no frame callbacks to a window covered by a fullscreen or maximized one, on a hidden workspace, or with the display off, so a vsync swap blocked the event loop the tray shares and the compositor called the app unresponsive. Pin the forks ZapFast, Spotifast, RekordFlash and TonePush share: crmne/egui apps-0.36 paces Wayland frames by frame callbacks (emilk/egui#8631) and crmne/winit apps-0.30 reports suspended as Occluded (rust-windowing/winit#4709). egui_kittest comes from the same revision so the tests share one egui.
| return false; | ||
| } | ||
|
|
||
| self.window.wl_surface().commit(); |
There was a problem hiding this comment.
winit should never commit on behalf of the user when there's a possibility of user rendering, which there's, so such approach doesn't work.
What works, perhaps, is once you get Suspended, do RequestRedraw and after that request submit Occluded, so the event is kind of delayed with the hope that user redrawn because they got asked to do so, so delivering Occluded would be once we know that user kind-of committed the occluded state.
Hyprland sends no frame callbacks to a window covered by a fullscreen or maximized one, on a hidden workspace, or with the display off, so a vsync swap blocked the event loop the tray shares and the compositor called the app unresponsive. Pin the forks ZapFast, Spotifast, RekordFlash and TonePush share: crmne/egui apps-0.36 paces Wayland frames by frame callbacks (emilk/egui#8631) and crmne/winit apps-0.30 reports suspended as Occluded (rust-windowing/winit#4709). egui_kittest comes from the same revision so the tests share one egui.
Hyprland sends no frame callbacks to a window covered by a fullscreen or maximized one, on a hidden workspace, or with the display off, so a vsync swap blocked the event loop the tray shares and the compositor called the app unresponsive. Pin the forks ZapFast, Spotifast, RekordFlash and TonePush share: crmne/egui apps-0.36 paces Wayland frames by frame callbacks (emilk/egui#8631) and crmne/winit apps-0.30 reports suspended as Occluded (rust-windowing/winit#4709). egui_kittest comes from the same revision so the tests share one egui.
|
Thanks, understood about not committing on the user's behalf. I tested the redraw-then- So the dialog is the app's job ( |
* Part of #5136 * Closes #8414 On Wayland a compositor may stop sending frame callbacks to a window it isn't showing, without telling the client that the window is occluded. Hyprland does this when another window on the same workspace goes fullscreen or maximized. With the glow backend, eframe then blocks in a vsync `eglSwapBuffers` until the window is shown again. Nothing runs, pings go unanswered, and the compositor reports the app as not responding. With wgpu, `pre_present_notify` keeps eframe from blocking, but `RedrawRequested` never arrives, so `App::logic` stops running. In rust-windowing/winit#4668 the winit maintainers said that winit should keep holding `RedrawRequested` until the frame callback arrives, and that a timeout belongs in the toolkit. This PR adds that timeout: * glow calls `Window::pre_present_notify` before swapping, like egui-wgpu already does, so winit paces `RedrawRequested` by frame callbacks. * On Wayland, glow swaps with interval 0. The frame callbacks already pace it. With the NVIDIA driver (egl-wayland2), a vsync swap also blocks forever for a window that starts on a hidden workspace: the compositor sends a frame callback for the first buffer but never presents it, and the next swap waits for that buffer. * If a `RedrawRequested` we asked for hasn't arrived within 250 ms, eframe runs `App::logic` without an egui pass, the same as for other hidden windows. Once the compositor sends a frame callback again, the window paints normally. `glow_options.vsync` therefore has no effect on Wayland. That's what vsync means there anyway, and the docs now say so. ## Testing I tested with a small probe that calls `request_repaint` on every `App::logic` call and counts `logic` and `ui` calls. It ran on Hyprland 0.56.2 with NVIDIA and a headless output, and I counted pings and pongs with `WAYLAND_DEBUG=1`. Each phase lasted 10 s: | Scenario | Before | After | | --- | --- | --- | | Visible (glow and wgpu) | 60 fps | 60 fps | | Covered by a fullscreen window, glow | blocked in `eglSwapBuffers`, 0 pings answered | about 4 `logic`/s, no paints, all pings answered | | Covered by a maximized window, glow | same (Hyprland stops the callbacks) | same as above | | Covered, wgpu | responsive, but 0 `logic` calls | about 4 `logic`/s | | Window on a hidden workspace | glow blocks | about 4 `logic`/s, all pings answered | | Started on a hidden workspace, glow | blocked on the second swap | about 4 `logic`/s, all pings answered | | Shown again | resumes | resumes on the next frame callback | | Idle | 0 CPU | 0 CPU | X11 and other platforms: I only checked that they compile. Nothing changes there except that `run_logic` may run if a `RedrawRequested` takes more than 250 ms to arrive, which only calls `App::logic` one extra time. When winit reports `Occluded` on Wayland (rust-windowing/winit#4709), eframe's existing occluded handling takes over for hidden workspaces. This timeout still covers windows the compositor hides without suspending them. * [x] I have followed the instructions in the PR template --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> Co-authored-by: Lucas Meurer <hi@lucasmerlin.me>
On Wayland, a compositor stops sending frame callbacks to a window it has suspended (xdg-shell v6
suspended, sent for example when the window is on a hidden workspace or the outputs are off). A client that presents with vsync then blocks until the window is shown again, which freezes its event loop.Winit used to report this state as
WindowEvent::Occluded, but #3441 removed it: every configure also requested a redraw so the acknowledgement would be committed, and drawing on the configure that suspends the window blocked. This follows the design in #3442:Occluded(true)andOccluded(false)are emitted when thesuspendedstate is added or removed.WindowExtWayland::notify_presentation_paused(). An app calls it after it has stopped presenting onOccluded(true). Winit then commits the surface itself for the pending configure (sctk has already acked it) and for every later configure while the window stays suspended, instead of asking for a redraw. The configure that ends the suspension asks for a redraw as before, and the nextOccluded(true)needs a new call.Occludeddocs now cover Wayland and this contract, including apps that keep presenting at a low rate while occluded (swap interval 0, nopre_present_notify).Testing: a softbuffer probe hid its window three times per run, on headless Jay (with hidden windows kept in transactions, stock 50 ms timeout) and on Hyprland 0.56.2 (headless output):
Hyprland showed the expected
Occludedevents and commits. Sway doesn't sendsuspended. Compositors older than xdg_wm_base v6 never send it either, so nothing changes there.changelogmodule if knowledge of this change could be valuable to users